import re
import paramiko
import requests
import json
import pymysql
import time

from myapp.models import *


def save_step_log(step,res):
    try:
        DB_DIY_steps.objects.filter(id=step.id).update(log=res.text)
    except:
        DB_DIY_steps.objects.filter(id=step.id).update(log=res.json())


#### 统一返回值

# request 请求
def do_request(step, session=""):
    # step.timeout 用上

    final_res = {"data": {}, "msg": "", "code": 0}

    print(f"do_step.py 接收到的 step == > {step}")

    method = step.request_method
    url = step.request_url
    body = step.request_body
    headers = step.request_headers
    body_method = step.request_body_method
    sign = step.request_sign
    cert = step.request_cert

    # 处理header
    headers = json.loads(headers)
    # 如果有特殊的请求头可以帮助你绕过黑名单、风控等限制，要添加上
    headers.update({})
    print(f"处理后的 headers ==>{headers}")

    # 处理url， 安全过滤用(防止有人通过平台对生成环境造成恶意破坏)
    if 'xxx.com' in url:
        final_res["code"] = -20
        final_res["msg"] = "命中安全防御"

        return final_res

    # 实际发送请求  后续升级 post/ get/ put /delete file
    if method in ["get", "Get", "GET"]:
        if session:
            print("需要分组")
            res = session.get(url, headers=headers)
        else:
            res = requests.get(url, headers)

    elif method in ["post", "Post", "POST"]:
        # 处理请求体
        if body in ['', " ", 'None', None]:  # 防止出现body输入框只敲了个空格或者未输入造成的request请求异常
            body = '{}'
        # 处理请求体类型
        if body_method == 'raw_json':
            headers["Content-Type"] = 'application/json'
            body = json.loads(body)
        elif body_method == 'form-data':
            try:
                body = json.loads(body)
            except:
                body = {}
        elif body_method == 'x-www-form-urlencoded':  # aaa=111&bbb=222

            headers["Content-Type"] = "application/w-www.form-urlencoded"

        else:
            body = {}
        if session:
            res = session.post(url, headers, data=body)
        else:
            res = requests.post(url, headers, data=body)

    # print(res.request.headers)

    final_res["code"] = 0
    final_res["msg"] = "请求成功"
    final_res["data"] = res
    # print(res.text)
    # print(step.id)

    save_step_log(step, res)
    return final_res
    # try:
    #     return res.json()
    # except:
    #     return res.text

    # return {"data": {"gzh": "测试开发行货", "id": 80009999}}  模拟数据


# def do_sql(step):
#     return (("1", "2", "3", "4"), ("a", "b", "c", "d"))


# def do_linux(step):
#     return 'abcdeff'


class DO_sql_class(object):
    def __init__(self, host, port, username, password):
        '接收整理所需的必要参数'
        self.host = host
        self.port = port
        self.username = username
        self.password = password

    def connect(self, db_name):
        '连接数据库'
        self.conn = pymysql.connect(host=self.host, port=int(self.port),
                                    user=self.username, password=self.password,
                                    database=db_name,
                                    charset="utf8")
        # 得到一个可以执行SQL语句的光标对象
        self.cursor = self.conn.cursor()

    def send(self, sql_body):
        '发送sql语句，获取返回结果'
        tmp_res = self.cursor.execute(sql_body)
        return tmp_res

    def close(self):
        '关闭sql链接'
        try:
            self.cursor.close()
            self.conn.close()
        except:
            pass


# 请求sql
def do_sql(step):
    '''
    sql_fixture = models.CharField('sql组件id',max_length=10,null=True,blank=True,default='')
    sql_body = models.CharField('sql语句体',max_length=2000,null=True,blank=True,default='')
    '''
    final_res = {"data": {}, "msg": "", "code": 0}
    sql_view = DB_SQL.objects.filter(id=step.sql_fixture)[0]
    host = sql_view.host
    port = sql_view.port
    db_name = sql_view.db_name
    username = sql_view.username
    password = sql_view.password
    # 创建数据库实例
    sql_db = DO_sql_class(host=host, port=port, username=username, password=password)
    res = []
    try:
        # 链接数据库
        sql_db.connect(db_name=db_name)
        # 发送sql语句
        res = sql_db.send(sql_body=step.sql_body)  # res 就是二元元组
        # 断开链接
        sql_db.close()

        final_res['msg'] = '构造成功'
        final_res['data'] = res
        save_step_log(step, res)
        return final_res

        # 发送sql 语句（考虑架构的复杂性，sql 类型的步骤只支持一步执行一条sql）
        # for i in step.sql_body.split("|||"):
        #     tmp_res = sql_db.send(sql_body=i)
        #     # 存储结果返回给上游
        #     res.append(tmp_res)
        # # 断开链接
        # sql_db.close()


    except:
        sql_db.close()
        final_res['msg'] = '数据库执行失败'
        final_res['code'] = -111
        save_step_log(step, res)
        return final_res


class DO_linux_class(object):
    def __init__(self, host, port, username, password, timeout=60):
        '接收整理必要参数'
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.timeout = timeout

    def connect(self):
        '链接远程服务器'
        try_count = 3
        while True:
            try:
                self.client = paramiko.Transport(sock=(self.host, int(self.port)))
                self.client.connect(username=self.username, password=self.password)
                self.chan = self.client.open_session()
                self.chan.settimeout(self.timeout)
                self.chan.get_pty()  # 虚拟终端
                self.chan.invoke_shell()  # 激活
                return
            except Exception as e:
                print(e)
                if try_count != 0:
                    print('链接失败，进行重试！')
                    try_count -= 1
                else:
                    print('重试次数耗尽，结束程序!')
                    raise

    def send(self, linux_body):
        '发送命令（多条命令通过 ||| 分割）'
        for i in linux_body.split('|||'):
            i += '\r'  # linux 里 \r 是换行
            p = re.compile(r'$')  # $ 是每个命令行的起始
            result = ''  # 返回结果
            self.chan.send(i)  # 发送命令
            while True:
                time.sleep(0.5)
                tmp = self.chan.recv(65535)
                tmp = tmp.decode('utf-8')
                result += tmp
                if p.search(tmp):  # 当命令执行结束后，如果能匹配到$ ，则表示到了命令执行完了，可以输入下一个命令了。举例 输入 ls
                    break
            time.sleep(1)

    def close(self):
        '断开链接'
        try:
            self.chan.close()
            self.client.close()
        except:
            pass


# 请求liunx服务器  需要执行服务器上的某个脚本 等才需要用到
def do_linux(step):
    '''
    linux_fixture = models.CharField('linux组件id', max_length=10, null=True, blank=True, default='')
    linux_body = models.CharField('linux语句体', max_length=2000, null=True, blank=True, default='')
    '''
    final_res = {"data": {}, "msg": "", "code": 0}
    linux_view = DB_LINUX.objects.filter(id=step.linux_fixture)[0]
    host = linux_view.host
    port = linux_view.port
    username = linux_view.username
    password = linux_view.password
    # 实例化
    linux_c = DO_linux_class(host=host, port=port, username=username, password=password)
    # 连接
    try:
        linux_c.connect()
        # 发送命令
        res = linux_c.send(step.linux_body)  # res很可能是大段字符串
        # 断开链接
        linux_c.close()
        final_res['msg'] = '构造成功'
        final_res['data'] = res
        save_step_log(step, res)
        return final_res
    except:
        linux_c.close()
        final_res['msg'] = '服务器失败'
        final_res['code'] = -111
        save_step_log(step, res)
        return final_res

